
__author__="Hong Phuc Bui, Sarah Theobald "
__date__ ="$Nov 16, 2011 10:54:44 AM$"


from matrix import Matrix


# init vectors:

def pHash(matrix): #
	'''
		Diese Funktion implementiert die :math:`P` Hash. Sie berechnet den :math:`P` Hashwert einer
		Matrix.

		:param matrix: die Matrix, deren :math:`P` Hash berechnet wird.

		.. Note::
			Die Funktion aendert den Zustand der ``matrix``
	'''

	for i in range(0,matrix.__ROUNDS__):
		matrix.addRoundConstant(i,True)
		matrix.subBytes()
		matrix.shiftBytes(matrix.sigma_p)
		matrix.mixBytes()

def qHash(matrix): # implement the q-Hash part.
	'''
		Diese Funktion implementiert die :math:`Q` Hash. Sie berechnet den :math:`Q` Hashwert
		einer Matrix.

		:param matrix: die Matrix, deren :math:`Q` Hash berechnet wird.

		.. Note:: 
			Die Funktion aendert den Zustand der ``matrix``
	'''
	for i in range(0,matrix.__ROUNDS__):
		matrix.addRoundConstant(i,False)
		matrix.subBytes()
		matrix.shiftBytes(matrix.sigma_q)
		matrix.mixBytes()


def compress(h,m): # f Funktion in Spezifikation
	'''
		Die Funktion fuehrt die Operation :math:`P(h \oplus m) \oplus Q(m) \oplus h`
		aus.
		
		:param h: chain-Matrix (Ergebnis der letzten Kompression oder der Initvektor)
		:param m: message-Matrix  
	'''
	# pHash(chain ^ message) ^ qHash(message) ^ chain
	tmpMatrix = xOrMatrix(h, m)               # (h^m)
	pHash(tmpMatrix)                          #P(h^m)
	qHash(m)                                  #Q(m)
	tmpMatrix2 = xOrMatrix(tmpMatrix, m)      #P(h^m) ^ Q(m)
	compressMatrix = xOrMatrix(tmpMatrix2, h) #( P(h^m) ^ Q(m) ) ^ h  
	return compressMatrix


def xOrMatrix(chain, message): #
	'''
		Hilfsfunktion, um die :math:`\oplus` Verknuepfung der Matrizen durchzufuehren.
		
		.. Note::
			Die Parameter werden nicht veraendert.
	'''
	if chain.__COLS__ != message.__COLS__:
		raise ValueError("Two matrixes must be same type")

	resultMatrix = [] # array von zeile
	for i in range(0,chain.__ROWS__):
		tmp = [] # neue Zeile
		for j in range(0,chain.__COLS__):
			tmp.append(chain.matrix[i][j] ^ message.matrix[i][j])
		resultMatrix.append(tmp)
		
	if chain.__COLS__ == 8:
		matrixObject = Matrix(smallSize=True)
	else:
		matrixObject = Matrix(smallSize=False)
	matrixObject.matrix = resultMatrix
	return matrixObject
		

##########################################################################
def groestlPad(inputStr, l):#
	'''
		Diese Funktion bildet das Padding der Spezifikation ab. 
		
		:param inputStr: String, der gepadded wird
		:param l: bei :math:`8\\times 8` Matrix ist die Laenge 512 Bit, bei
			:math:`8\\times 16` Matrix ist die Laenge 1024 Bit
		:return: den gepaddeden String
	'''
	# calculate length of input
	# the length of input is in Byte, also in Bit is a multiple
	# of 8. We assume that the last byte is always complete.
	N = len(inputStr)*8
	# if length of inputStr is a multiple of l, do nothing
	
	padByteArray = []
	# "First, it appends the bit "1" to x.
	# then, it appends $w=-N-65 mod l$ '0' bits,
	w = (-N -65) % l
	# also the length of appended bits is 1+ w
	middle_pad_len = w+1
	padByteArray.append(int("10000000",2))
	i = 0
	while i < ( middle_pad_len/8 - 1):
		padByteArray.append(0)
		i += 1
		
	#print "padByteLen in bit: %d" %(len(padByteArray)*8)
	#print "middle_pad_len:    %d" %middle_pad_len
	
	################################################################
	
	# finally, it appends a 64-bit representation of $(N + w + 65)/l$
	rest = (N + w + 65)/l
	#print "(N + w +65)/l = %d" % rest
	restArray = []
	count = 1
	#	construct the 64-bit representation of (N + w +65)/l
	#	and save it in restArray 
	restArray.insert(0,rest % (2**8))
	rest = rest / (2**8)
	
	while  rest > (2**8):
		restArray.insert(0,rest % (2**8))
		rest = rest / (2**8)
		count +=1
	
	while count < 8:
		count +=1
		restArray.insert(0,0)
	
	
	for i in restArray:
		padByteArray.append(i)	
	
	#print "1 + w + 64 = %d" % (1+w+64)
	#print "lop        = %d" % (len(padByteArray)*8)
	
	tmp = map (chr,padByteArray) 
	tmp = "".join(tmp)
	return inputStr +tmp

def trunc(n,x):#
	'''
		Diese Funktion fuehrt die Operation :math:`\\text{trunc}_{n}(P(x) \oplus x)` aus.
			
		:param n: Der Laenge der Ausgabe in Byte
		:param x: Die Hash-Matrix
		:return: Eine Tuple :math:`(r,o)`, dabei :math:`r` ist das Ergebnis der Operation
			:math:`\\text{trunc}_{n}(P(x) \oplus x)` , und
			:math:`o` das Erbenis der Operation  :math:`P(x) \oplus x)` (Fuer debug gedacht)
		:rtype: Eine 2-er-Tupel von Array und Matrix von Bytes.

	'''
	original = x.clone()
	pHash(x)
	outputTransform = xOrMatrix(x, original)
	result = []
	count = 0;
	for i in range(outputTransform.__COLS__ - 1, -1, -1):
		for j in range(outputTransform.__ROWS__ - 1, -1, -1):
			result.insert(0,outputTransform.matrix[j][i])
			count += 1;
			if count >= n:
				return (result,outputTransform)
	pass

def int2Hex(b):#
	'''
		Hilfsfunktnion um ein Byte in die Darstellung von zwei hexadezimal Ziffer
		zu konvertieren.

		:param b: Byte zur Konvertierung.
		:return: Die Darstelung von ``b`` in hexadezimal in String Format.
		:rtype: ``str``

	'''
	tmp = hex(b)
	tmp = tmp.lstrip("0x")
	while len(tmp) < 2:
		tmp = "0"+tmp
	return tmp

def array2str(array):#
	'''
		Konvertiert ein Array von Bytes in einen String. Jede Byte wird in
		die Darstellung von zwei hexadezimal Ziffer konvertiert.

		:param array: der Array zur Konvertierung
		:rtype: ``str``

	'''
	array = map(int2Hex,array)
	return "".join(array)



def groestlHash(message,length):#
	'''
		API-Schnittstelle, um Groestl Hash zu berechnen.
		
		:param message: Eingabe-String
		:param length: Laenge des zurueckgegebenen Hashwerts in bit
		:rtype: ``str``
		:raise ValueError: Wenn ``length`` kein Vielfaches von 8 ist oder
			``length`` groeser als 512 ist, wird ``ValueError``
			verursacht.

	'''
	
	if (length % 8):
		raise ValueError("length must be multiple of 8")
	if length > 512:
		raise ValueError("length cannot greater than 512")
		
	small = None
	prefixCount = 0
	if length <= 256:
		small = True
		prefixCount = 62
	else:
		small = False
		prefixCount = 126
		
	iv = None
	if length <= 224:
		iv = "\x00"*prefixCount + "\x00" + "\xe0"
	elif (length <= 256) and (length > 224):
		iv = "\x00"*prefixCount + "\x01" + "\x00" 
	elif (length <= 384) and (length > 256):
		iv = "\x00"*prefixCount + "\x01" + "\x80"
	elif (length <= 512) and (length > 384):
		iv = "\x00"*prefixCount + "\x02" + "\x00"
	
	chainMatrix = Matrix(small)
	chainMatrix.mapping(iv)
	
	padStr = groestlPad(message, chainMatrix.__BLOCK_SIZE__)
	numOfBlock = len(padStr) / (chainMatrix.__BLOCK_SIZE__/8)
	# numOfBlock = 1
	step = chainMatrix.__BLOCK_SIZE__ / 8
	
	for i in range(0, numOfBlock):
		msgMatrix = Matrix(small)
		blockMsg = padStr[i*step:step*(i+1)]
		
		msgMatrix.mapping(blockMsg)
		chainMatrix = compress(chainMatrix, msgMatrix)
	
		
	# bis hier ist die P und Q permutation OK
	(r,m) = trunc(length/8, chainMatrix)
	x = array2str(r)
	return x
	pass

